package com.lam.common.utils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.lam.common.exception.BaseException;
import com.lam.common.exception.BusinessException;
import com.lam.common.utils.reflect.BeanGetMethodInvoke;

/**
 * 一系列bean工具类
 * @author lam
 * @date 2016年3月24日 下午3:16:34
 */
public class BeanUtil extends BeanUtils {

	private static final Logger logger = LoggerFactory.getLogger(BeanUtil.class);

	private static final String GETTER_PREFIX = "get";
	private static final String GETTER_PREFIX0 = "is";
	private static final String SETTER_PREFIX = "set";
	public static final String OBJECT_METHOD_SEPARATOR = ".";
	
	private BeanUtil() {
		throw new IllegalStateException("Utility class");
	}
	
	public static boolean equals(Object o1, Object o2, String ... fields) {
    	if(o1 == null || o2 == null || o1.getClass() != o2.getClass()) {
    		return false;
    	}
    	if(o1 == o2) {
    		return true;
    	}
    	Class<? extends Object> clazz = o1.getClass();
    	if(isBaseClassType(clazz)) {
    		return o1.equals(o2);
    	}
    	
    	if(CollectionUtil.isEmpty(fields)) {
    		return false;
    	}
    	
    	try {
			Method getter;
			Object v1;
			Object v2;
			for (String field : fields) {
				getter = getGetterMethod(clazz, field);
				v1 = getter.invoke(o1);
				v2 = getter.invoke(o2);
				if(!equals(v1, v2)) {
					return false;
				}
			}
			return true;
		} catch (Exception e) {
			logger.error("对象比较异常："+e.getMessage(), e);
			throw new BaseException("对象比较异常", e);
		} 
    }
	
	public static boolean isBaseClassType(Class<? extends Object> clazz) {
		return clazz == String.class || clazz == Integer.class || clazz == Short.class || clazz == Long.class 
				|| clazz == Double.class || clazz == Float.class || clazz ==BigDecimal.class || clazz == Byte.class
				|| clazz == Boolean.class;
	}

	public static void copyProperties(Object dest, Object orig) {
		try {
			org.springframework.beans.BeanUtils.copyProperties(dest, orig);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException("转换对象失败");
		}
	}

	public static Object getValueByName(Object bean, String name) {
		
		if(bean == null || StringUtils.isBlank(name)){
			return null;
		}
		BeanGetMethodInvoke md = new BeanGetMethodInvoke(bean.getClass(), name);
		try {
			return md.invoke(bean);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * 如果不存在该字段则返回null
	 *
	 * @param bean
	 * @param field
	 * @return
	 * @date 2017年4月11日
	 * @author lam
	 */
	public static Object getValue(Object bean, Field field) {
		
		try {
			// 获得get方法
			Method getMethod = new PropertyDescriptor(field.getName(), bean.getClass()).getReadMethod();
			return getMethod.invoke(bean);
		} catch (Exception e) {
			logger.debug(e.getMessage(), e);
			return null;
		}
	}
	
	/**
	 * 如果不存在该字段则返回null
	 *
	 * @param bean
	 * @param field
	 * @return
	 * @date 2017年4月11日
	 * @author lam
	 */
	public static void setValue(Object bean, Field field, Object value) {
		
		setValue(bean, field.getName(), value);
	}
	
	/**
	 * 如果不存在该字段则返回null
	 *
	 * @param bean
	 * @param name
	 * @return
	 * @date 2017年4月11日
	 * @author lam
	 */
	public static void setValue(Object bean, String name, Object value) {
		
		try {
			// 获得set方法
			Method setMethod = new PropertyDescriptor(name, bean.getClass()).getWriteMethod();
			setMethod.invoke(bean, value);
		} catch (Exception e) {
			logger.debug(e.getMessage(), e);
		}
	}

	/**
	 * 获取指定类的所有字段，包括从父类继承过来的（包括静态字段和其他字段）
	 *
	 * @param clazz
	 * @return
	 * @date 2017年4月10日
	 * @author lam
	 */
	private static List<Field> getFields(Class<?> clazz) {
		if (clazz.isAssignableFrom(Object.class)) {
			return Collections.emptyList();
		}
		List<Field> fields = new ArrayList<>();
		fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
		if(clazz.getSuperclass() != null) {
			fields.addAll(getFields(clazz.getSuperclass()));
		}
		return fields;
	}

	/**
	 * 获取指定类的所有业务字段，包括从父类继承过来的
	 *
	 * @param clazz
	 * @return
	 * @date 2017年4月10日
	 * @author lam
	 */
	public static List<Field> getBusinessFields(Class<?> clazz) {
		List<Field> fdsTmp = getFields(clazz);
		List<Field> fds = new ArrayList<>();
		for (Field field : fdsTmp) {
			int mdf = field.getModifiers();
			// 默认,public,private,protected
			if (mdf == 0 || mdf == 1 || mdf == 2 || mdf == 4) {
				fds.add(field);
			}
		}
		return fds;
	}

	/**
	 * 获取指定类的所有字段名称，包括从父类继承过来的
	 *
	 * @param clazz
	 * @return
	 * @date 2017年4月10日
	 * @author lam
	 */
	public static List<String> getFieldNames(Class<?> clazz) {
		List<Field> fields = getBusinessFields(clazz);
		List<String> fieldNames = new ArrayList<>(fields.size());
		for (Field field : fields) {
			fieldNames.add(field.getName());
		}
		return fieldNames;
	}

	/**
	 * 根据字段批量获取getter方法
	 *
	 * @param clazz
	 * @param fields
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static Method[] getGetterMethods(Class<?> clazz, String[] fields) {
		if (clazz == null || fields == null || fields.length < 1) {
			throw new BusinessException("类和字段不能为空");
		}
		Method[] mds = new Method[fields.length];
		for (int i = 0; i < fields.length; i++) {
			mds[i] = getGetterMethod(clazz, fields[i]);
		}
		return mds;
	}
	
	public static Field getField(Class<?> clazz, String fieldName) {
		try {
			return clazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException | SecurityException e) {
			logger.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 根据字段批量获取setter方法
	 *
	 * @param clazz
	 * @param fields
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static Method[] getSetterMethods(Class<?> clazz, String[] fields) {
		if (clazz == null || fields == null || fields.length < 1) {
			throw new BusinessException("类和字段不能为空");
		}
		Method[] mds = new Method[fields.length];
		for (int i = 0; i < fields.length; i++) {
			mds[i] = getSetterMethod(clazz, fields[i]);
		}
		return mds;
	}

	/**
	 * 根据字段获取getter方法
	 *
	 * @param clazz
	 * @param field
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static Method getGetterMethod(Class<?> clazz, String field) {
		try {
//			return new PropertyDescriptor(field, clazz).getReadMethod();
			return clazz.getMethod(getGetterName(field));
		} catch (Exception e0) {
			try {
				return clazz.getMethod(getGetterName0(field));
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new BusinessException("获取类方法出错");
			}
		}
	}

	/**
	 * 根据字段获取setter方法
	 *
	 * @param clazz
	 * @param field
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static Method getSetterMethod(Class<?> clazz, String field) {
		try {
//			return new PropertyDescriptor(field, clazz).getWriteMethod();
			return clazz.getMethod(getSetterName(field), new Class[] {clazz.getDeclaredField(field).getType()});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException("获取类方法出错");
		}
	}

	/**
	 * 根据前缀和字段获取对应的方法
	 *
	 * @param prefix
	 * @param fieldName
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static String getMethodName(String prefix, String fieldName) {
		if (StringUtils.isBlank(prefix)) {
			return String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
		}
		return prefix + String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
	}

	/**
	 * 根据字段获取对应的getter方法
	 *
	 * @param fieldName
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static String getGetterName(String fieldName) {
		return getMethodName(GETTER_PREFIX, fieldName);
	}
	
	public static String getGetterName0(String fieldName) {
		return getMethodName(GETTER_PREFIX0, fieldName);
	}

	/**
	 * 根据字段获取对应的setter方法
	 *
	 * @param fieldName
	 * @return
	 * @date 2016年10月14日
	 * @author lam
	 */
	public static String getSetterName(String fieldName) {
		return getMethodName(SETTER_PREFIX, fieldName);
	}

	/**
	 * 根据类型和方法名称调用Bean的方法
	 * @param beanType bean类
	 * @param methodName 方法名称
	 * @param params 方法的参数对象
	 * @param paramTypes 方法的参数对象类型，跟参数对象的顺序一一对应
	 * @return
	 * @date 2017年6月19日
	 * @author lam
	 */
	public static Object getMethodInvokeByBeanType(Class<?> beanType, String methodName, Object[] params, Class<?>[] paramTypes) {
		if(beanType == null || Object.class.equals(beanType)){
			logger.error("parameter missing:beanType");
			throw new BusinessException("parameter missing:beanType");
		}
		if (StringUtils.isBlank(methodName)) {
			logger.error("parameter missing:methodName");
			throw new BusinessException("parameter missing:methodName");
		}

		Object obj = null;
		try {
			Object bean = SpringUtils.getBean(beanType);
			Method method = bean.getClass().getMethod(methodName, paramTypes);
			obj = method.invoke(bean, params);
		} catch (Exception e) {
			logger.error("根据BeanType获取数据时异常", e);
			throw new BusinessException("根据BeanType获取数据时异常", e);
		}
		return obj;
	}

	/**
	 * 根据beanId和方法名称调用Bean的方法
	 * @param beanAndMethod bean和方法名称，用英文点.分隔
	 * @param params 方法的参数对象
	 * @param paramTypes 方法的参数对象类型，跟参数对象的顺序一一对应
	 * @return
	 * @date 2017年6月19日
	 * @author lam
	 */
	public static Object getMethodInvokeByBeanId(String beanAndMethod, Object[] params, Class<?>[] paramTypes) {
		if (StringUtils.isBlank(beanAndMethod) || beanAndMethod.indexOf(OBJECT_METHOD_SEPARATOR) < 0) {
			logger.error("parameter missing: beanAndMethod");
			return null;
		}
		String[] beanAndMethodArr = beanAndMethod.split("\\.");
		if (StringUtils.isBlank(beanAndMethodArr[0]) || StringUtils.isBlank(beanAndMethodArr[1])) {
			logger.error("parameter missing: beanAndMethod");
			return null;
		}
		Object obj = null;
		try {
			Object bean = SpringUtils.getBean(beanAndMethodArr[0]);
			Method method = bean.getClass().getMethod(beanAndMethodArr[1], paramTypes);
			obj = method.invoke(bean, params);
		} catch (Exception e) {
			logger.error("根据BeanId获取数据时异常", e);
			throw new BusinessException("根据BeanId获取数据时异常");
		}
		return obj;
	}

	public static Object[] getValues(Object obj, String[] fieldNames) {
		try {
			Object[] ps = new Object[fieldNames.length];
			for (int i = 0; i < fieldNames.length; i++) {
				ps[i] = getValueByName(obj, fieldNames[i]);
			}
			return ps;
		} catch (Exception e) {
			logger.error("根据字段名称批量获取对象字段值失败", e);
			throw new BusinessException("根据字段名称批量获取对象字段值失败", e);
		}
	}

	public static Object[] getValues(Object obj, List<Field> fields) {
		try {
			Object[] ress = new Object[fields.size()];
			int i = 0;
			for (Field field : fields) {
				ress[i++]=getValue(obj, field);
			}
			return ress;
		} catch (Exception e) {
			logger.error("根据字段批量获取对象字段值失败", e);
			throw new BusinessException("根据字段批量获取对象字段值失败", e);
		}
	}

	/**
	 * 根据指定字段进行复制对象
	 * @param origin
	 * @param target
	 * @param fields
	 * @date 2017年6月28日
	 * @author lam
	 */
	public static void copyByField(Object origin, Object target, String ... fields){
		if(fields == null || fields.length == 0){
			logger.error("未配置要复制的映射字段");
			throw new BusinessException("未配置要复制的映射字段");
		}
		String[][] fieldArr = StringUtils.splitFields(fields);
		int len = fieldArr[0].length;
		String[] originFields = fieldArr[0];
		String[] targetFields = fieldArr[1];
		Object val;
		for (int i = 0; i < len; i++) {
			val = getValueByName(origin, originFields[i]);
			if(val == null){
				continue;
			}
			setField(target, targetFields[i], val);
		}
	}

	public static void setField(Object obj, String fieldName, Object value){
		try {
			setProperty(obj, fieldName, value);
		} catch (IllegalAccessException | InvocationTargetException e) {
			logger.error("copy时赋值失败", e);
		}
	}
	
	/** 只读字段上面的注解，不读方法上的注解  */
	public static final int METHOD_TYPE_NONE = 0;
	/** 读字段和getter方法上的注解  */
	public static final int METHOD_TYPE_GETTER = 1;
	/** 读字段和setter方法上的注解  */
	public static final int METHOD_TYPE_SETTER = 2;
	
	public static <A extends Annotation> Map<Field, A> getFieldAnnotation(Class<?> clazz, Class<A> annotationType) {
		return getFieldAnnotation(clazz, annotationType, METHOD_TYPE_NONE, false);
	}
	
	public static <A extends Annotation> Map<Field, A> getFieldAnnotation(Class<?> clazz, Class<A> annotationType, boolean useIgnore) {
		return getFieldAnnotation(clazz, annotationType, METHOD_TYPE_NONE, useIgnore);
	}
	
	public static <A extends Annotation> Map<Field, A> getFieldAnnotation(Class<?> clazz, Class<A> annotationType, int methodType) {
		return getFieldAnnotation(clazz, annotationType, methodType, false);
	}
	
	public static <A extends Annotation> Map<Field, A> getFieldAnnotation(Class<?> clazz, Class<A> annotationType, int methodType, boolean useIgnore) {
		List<Field> fieldList = getSimpleField(clazz, useIgnore);
		Map<Field, A> fieldData = new LinkedHashMap<>();
		A a;
		for (Field field : fieldList) {
			a = getFieldAnnotation(field, annotationType, methodType);
			if (a != null) {
				fieldData.put(field, a);
			}
		}
		return fieldData;
	}
	
	public static <A extends Annotation> List<Field> getSimpleField(Class<?> clazz, boolean useIgnore) {
		try {
			List<Field> fieldList = getBusinessFields(clazz);
			List<Field> newList = new ArrayList<>();
			for (Field field : fieldList) {
				if(useIgnore) {
					JsonIgnore jsonIgnore = field.getAnnotation(JsonIgnore.class);
					if(jsonIgnore != null && jsonIgnore.value()) {
						continue;
					}
				}
				int mdf = field.getModifiers();
				// 默认,public,private,protected
				if (mdf == 0 || mdf == 1 || mdf == 2 || mdf == 4) {
					newList.add(field);
				}
			}
			return newList;
		} catch (Exception e) {
			logger.error("获取类字段注解异常", e);
			throw new BaseException("系统异常");
		}
	}
	
	public static <A extends Annotation> A getFieldAnnotation(Field field, Class<A> annotationType, int methodType) {
		try {
//			A a = field.getAnnotation(annotationType);
			A a = AnnotationUtils.getAnnotation(field, annotationType);
			if (a == null && methodType > 0) {
				
				//PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), field.getDeclaringClass());
				Method method = null;
				if(methodType == METHOD_TYPE_GETTER) {
//					method = propertyDescriptor.getReadMethod();
					method = getGetterMethod(field.getDeclaringClass(), field.getName());
				}
				if(methodType == METHOD_TYPE_SETTER) {
//					method = propertyDescriptor.getWriteMethod();
					method = getSetterMethod(field.getDeclaringClass(), field.getName());
				}
				if(method != null) {
					a = AnnotationUtils.findAnnotation(method, annotationType);
				}
			}
			return a;
		} catch (Exception e) {
			logger.error(String.format("获取类字段注解异常, field:%s.%s", field.getDeclaringClass(), field.getName()), e);
			throw new BaseException("获取类字段注解异常");
		}
	}
	
	public static Class<?>[] getActualType(Type genericType){
		
		if(genericType == null) {
			return null;
		}
		
		// 需要获取字段类型中的泛型信息，所以要进一步判断类型是否是参数化类型ParameterizedType
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取泛型类型信息
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            if(CollectionUtil.isEmpty(actualTypeArguments)) {
            	return null;
            }
            
            Class<?>[] classArr = new Class<?>[actualTypeArguments.length];
            for (int i = 0; i < actualTypeArguments.length; i++) {
            	if(actualTypeArguments[i] instanceof TypeVariable) {
            		TypeVariable<?> typeVariable = (TypeVariable<?>)actualTypeArguments[i];
            		classArr[i] = (Class<?>)typeVariable.getGenericDeclaration();
            	}else {
            		classArr[i] = (Class<?>)actualTypeArguments[i];
            	}
			}
        	return classArr;
        }
        return null;
	}
	

}


